[CmdletBinding()]
Param(
    [Alias('h')]
    [switch]$help,
    [Alias('f')]
    [string]$File = "autoinstall.json"
)

# 脚本全局配置
$script:JSON = [System.Object]
$script:NAME = [string]$MyInvocation.MyCommand.Name

# 设置进程名称（感谢JacquesFS）
if ($MyInvocation.MyCommand.CommandType -ne "ExternalScript") {
    $script:NAME = Split-Path -Leaf ([Environment]::GetCommandLineArgs()[0])
}

# 解析Json文件设置JSON变量
function Import-Json {
    if (-NOT (Test-Path $File -PathType Leaf)) {
        Read-HostPure "未找到配置文件: [$File], 请检查路径是否正确。`n按任意键继续..." -ForegroundColor Red
        exit 1
    }
    $script:JSON = Get-Content $File -Raw | ConvertFrom-Json
}

# 显示帮助
function Show-Help {
    if ($help -or [string]::IsNullOrEmpty($File)) {
        Write-Host "用法: $script:NAME [-c] [path]"
        Write-Host "    在新系统上部署已安装的软件"
        Write-Host "    1. 自动创建快捷方式"
        Write-Host "    2. 写入目录到环境变量"
        Write-Host "    3. 自动安装安装包"
        Write-Host "选项:"
        Write-Host "    -h -help           显示此帮助"
        Write-Host "    -f -File <string>  json配置文件文件路径"
        Write-Host "备注:"
        Write-Host "    1. 文件编码为UTF-8"
        Write-Host "    2. pkgs列表支持通配符"
        Write-Host "    3. 安装程序自动添加'/Install /Silent'选项"
        Write-Host "    4. apps列表第一个键值必须完整(name,exe,bin)"
        Write-Host "JSON示例:"
        Write-Host "    {"
        Write-Host '      "ShortcutPath": "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs",'
        Write-Host '      "apps": [{"name": "Git","exe": "D:\\Git\\git-bash.exe","bin": "D:\\Git\\bin"}]'
        Write-Host '      "pkgs": ["D:\\Drivers\\*.exe"]'
        Write-Host '    }'
        exit 0
    }
}

# 以管理员身份运行
function Enter-Admin {
    param (
        # PSBoundParameters
        [System.Collections.Generic.Dictionary[System.String, System.Object]]$Dictionary
    )
    if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')) {
        # 如果不是管理员，请求以管理员身份重新运行
        $PSargs = $Dictionary.GetEnumerator() | ForEach-Object { "-$($_.Key) ""$($_.Value)""" }
        Start-Process powershell -verb runas -ArgumentList "/NoExit /c cd $PWD;.\$script:NAME $PSargs"
        exit 0
    }
}

# 检查所有文件/文件夹是否存在
function Test-AllPath {
    # ShortcutPath
    if ($script:JSON.ShortcutPath -and (Test-Path $script:JSON.ShortcutPath)) {
        Write-Host "快捷方式目录: [$($script:JSON.ShortcutPath)]" -ForegroundColor Green
    }
    else {
        Write-Host "未找到该目录: [$($script:JSON.ShortcutPath)]" -ForegroundColor Red
    }
    # exe
    $script:JSON.apps | Where-Object { $_.exe } | ForEach-Object {
        if (Test-Path $_.exe -PathType Leaf) {
            Write-Host "待部署: [$($_.exe)]" -ForegroundColor Green
        }
        else {
            Write-Host "未找到: [$($_.exe)]" -ForegroundColor Red
        }
    }
    # bin
    $script:JSON.apps | Where-Object { $_.bin } | ForEach-Object {
        if (Test-Path $_.bin) {
            Write-Host "待部署: [$($_.bin)]" -ForegroundColor Green
        }
        else {
            Write-Host "未找到: [$($_.bin)]" -ForegroundColor Red
        }
    }
    # pkgs
    $script:JSON.pkgs | Where-Object { $_ -and (Test-Path $_) } | Get-ChildItem | ForEach-Object {
        if (Test-Path $_ -PathType Leaf) {
            Write-Host "待安装: [$_]" -ForegroundColor Green
        }
        else {
            Write-Host "未找到: [$_]" -ForegroundColor Red
        }
    }
}

# 设置光标位置
function Set-CursorPosition {
    param (
        [int]$X,
        [int]$Y,
        [int]$RX = 0,
        [int]$RY = 0
    )
    if (-NOT $PSBoundParameters.ContainsKey('X')) {
        $X = $Host.UI.RawUI.CursorPosition.X + $RX
    }
    if (-NOT $PSBoundParameters.ContainsKey('Y')) {
        $Y = $Host.UI.RawUI.CursorPosition.Y + $RY
    }
    $Host.UI.RawUI.CursorPosition = New-Object System.Management.Automation.Host.Coordinates $X, $Y
}

# 读取输入后去除提示
function Read-HostPure {
    param (
        [string]$Prompt = "按任意键继续...",
        [string]$ForegroundColor = $Host.UI.RawUI.ForegroundColor,
        [switch]$NoNewline
    )
    Write-Host $Prompt -NoNewline -ForegroundColor $ForegroundColor
    $answer = Read-Host
    Set-CursorPosition -X 0 -RY -1
    Write-Host (' ' * ($Host.UI.RawUI.WindowSize.Width - 1)) -NoNewline
    Set-CursorPosition -X 0 -RY $( if ($NoNewline) { 0 } else { 1 } )
    return $answer
}

# 创建快捷方式到目录
function New-Shortcut {
    param (
        [Parameter(Mandatory = $true)]
        [string]$ShortcutName,
        [Parameter(Mandatory = $true)]
        [string]$TargetPath,
        [Parameter(Mandatory = $true)]
        [string]$ShortcutPath
    )
    # 若exe不存在，则退出
    if (-NOT (Test-Path $TargetPath)) {
        Write-Host "Not Found: [$TargetPath]" -ForegroundColor Red
        return
    }
    $ShortcutPath = Join-Path $ShortcutPath $($ShortcutName + ".lnk")
    # 使用绝对路径创建快捷方式，不管文件存不存在
    $ShortcutPath = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($ShortcutPath)
    # 若文件存在，跳过创建
    if (Test-Path $ShortcutPath -PathType Leaf) {
        Write-Host "Has Link: [$ShortcutPath]" -ForegroundColor Yellow
        return
    }
    # 创建WScript.Shell对象以创建快捷方式
    $shell = New-Object -ComObject WScript.Shell
    $shortcut = $shell.CreateShortcut($ShortcutPath)
    $shortcut.TargetPath = Convert-Path $TargetPath
    $shortcut.Save()

    Write-Host "Set Link: [$ShortcutPath] -> [$TargetPath]" -ForegroundColor Green
}

# 设置环境变量
function Set-EnvPATH {
    param (
        [Parameter(Mandatory = $true)]
        [string]$path
    )
    # 若目录不存在，退出
    if (-NOT (Test-Path $path)) {
        Write-Host "not found: [$path]" -ForegroundColor Red
        return
    }
    # 获取系统环境变量
    $currentPath = [System.Environment]::GetEnvironmentVariable('PATH', 'Machine')
    if ($null -eq $currentPath -or $currentPath -eq "") {
        # PATH为空，直接设置PATH（一般不会进入此if）
        Write-Host "系统变量为空" -ForegroundColor Yellow
        $currentPath = $path
    }
    else {
        if ($currentPath.Split(';').Contains($path)) {
            # PATH中包含路径，跳过
            Write-Host "Has PATH: [$path]" -ForegroundColor Yellow
            return
        }
        if ($currentPath[-1] -ne ';') {
            # 最后不是分号，添加分号在前
            $path = ';' + $path
        }
        $currentPath += $path
    }

    [System.Environment]::SetEnvironmentVariable('PATH', $currentPath, 'Machine')
    Write-Host "Set PATH: [$currentPath]" -ForegroundColor Green
}

# 遍历apps，部署程序
function Import-APPS {
    $script:JSON.apps | ForEach-Object {
        if ($_.exe -and $_.name) {
            New-Shortcut -ShortcutName $_.name -TargetPath $_.exe -ShortcutPath $script:JSON.ShortcutPath
        }
        if ($_.bin) {
            Set-EnvPATH -path $_.bin
        }
    }
}

# 遍历pkgs，安装程序，路径支持通配符
function Install-Pkgs {
    $script:JSON.pkgs | Where-Object { Test-Path $_ } | Get-ChildItem | ForEach-Object {
        Write-Host "Installing: [$_]"
        $process = Start-Process -FilePath $_ -ArgumentList "/Install /Silent" -Wait -PassThru
        # 回到上一行并清除
        Set-CursorPosition -X 0 -RY -1
        Write-Host (' ' * ($Host.UI.RawUI.WindowSize.Width - 1)) -NoNewline
        Set-CursorPosition -X 0
        if ($process.ExitCode -ne 0) {
            $code = $process.ExitCode
            if (($process.ExitCode -le -1000) -or (1000 -le $process.ExitCode)) {
                $code = $process.ExitCode.ToString().Substring(0, 3)
            }
            $formattError = "{0,-11}" -f "Error($code):"
            Write-Host ("$formattError [$_]") -ForegroundColor Red

            $answer = Read-HostPure "安装失败，是否继续？(Y/n): " -NoNewline
            if ($answer -and $answer.Trim().ToLower() -eq "n") {
                Write-Host "`n"
                exit 1
            }
        }
        else {
            Write-Host "Success:    [$_]" -ForegroundColor Green
        }
    }
}


Show-Help
Enter-Admin $PSBoundParameters
Import-Json
Test-AllPath

Read-HostPure "`n即将部署程序, 按按任意键继续..." -NoNewline | Out-Null
Import-APPS

Read-HostPure "`n即将安装安装包, 按任意键继续..." -NoNewline | Out-Null
Install-Pkgs
Read-HostPure "设备部署完成，开始享用吧" -ForegroundColor Green | Out-Null
exit 0
